﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Net.Sockets;
using TransportAbstractionLibrary.Common.BufferPool;
using TransportAbstractionLibrary.Common;

namespace TransportAbstractionLibrary.TCP
{
    sealed partial class TCPProtocolBase
    {
        Queue<byte[]> sendingQueue;
        volatile bool isSending;
        object sendingLocker;

        SocketEventArgs senderEventArgs;

        int sendingBufferSize;

        private void InitializeSend()
        {
            sendingQueue = new Queue<byte[]>();
            senderEventArgs = bufferManager.CheckOut();
            senderEventArgs.Initialize(this, EventArgsMode.Send);
            sendingBufferSize = senderEventArgs.Count;
            sendingLocker = new object();
        }

        private void LoopData(byte[][] data)
        {
            byte[] buffer = null;
            lock(sendingLocker)
            {
                if (data != null)
                {
                    if (isSending)
                    {
                        for (int i = 0; i < data.Length; i++)
                        {
                            sendingQueue.Enqueue(data[i]);
                        }
                        return;
                    }
                    else
                    {
                        isSending = true;
                        buffer = data[0];
                        for (int i = 1; i < data.Length; i++)
                        {
                            sendingQueue.Enqueue(data[i]);
                        }
                    }
                }
            }

            //we have our data lets send it!
            bool sendMore = true;
            while (sendMore)
            {
                if (buffer == null)
                {
                    lock(sendingLocker)
                    {
                        if (sendingQueue.Count > 0)
                        {
                            buffer = sendingQueue.Dequeue();
                        }
                        else
                        {
                            isSending = false;
                            return;
                        }
                    }
                }
                senderEventArgs.SetData(buffer);
                sendMore = !socket.SendAsync(senderEventArgs);
            }
        }

        public void Send(byte[] data)
        {
            int index = 0;
            int parts = data.Length / sendingBufferSize;
            if (data.Length % sendingBufferSize > 0)
            {
                parts++;
            }
            byte[][] temp = new byte[parts][];
            for (int i = 0; i < parts; i++)
            {

                temp[i] = new byte[Math.Min(data.Length - index, sendingBufferSize)];
                Buffer.BlockCopy(data, index, temp[i], 0, temp[i].Length);
                index += temp[i].Length;
            }
            LoopData(temp);
        }

        void IAsyncHost.OnSent(SocketEventArgs args)
        {
            if (disposed)
            {
                return;
            }
            if (!socket.Connected)
            {
                Dispose();
                return;
            }
            if (senderEventArgs.SocketError != System.Net.Sockets.SocketError.Success)
            {
                throw new TALSocketException(this, "Send was not successfull, error: " + senderEventArgs.SocketError.ToString());
            }
            LoopData(null);
        }

        private void DisposeSend()
        {
            lock (sendingLocker)
            {
                try
                {
                    sendingQueue.Clear();
                    sendingQueue = null;
                    bufferManager.CheckIn(senderEventArgs);
                    senderEventArgs.Dispose();
                    senderEventArgs = null;
                }
                catch (SocketException ex)
                {
                    HandleException(ex);
                }
            }
        }
    }
}
